package com.lemeshko.mock;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;

public class Secretary implements Accountant {

	private final Map<String, BigDecimal> expenses = new HashMap<String, BigDecimal>();

	private final String EXPENSE_NAME_COLUMN_HEADER = "Name";
	private final String EXPENSE_AMOUNT_COLUMN_HEADER = "Amount";

	private final Printer printer;

	public Secretary(Printer printer) {
		super();
		this.printer = printer;
	}

	@Override
	public void addExpense(String expenseName, BigDecimal amount)
			throws IllegalArgumentException, NullPointerException {

		if (amount == null)
			throw new NullPointerException("Expense amount should be not null");

		if (amount.compareTo(BigDecimal.ZERO) <= 0)
			throw new IllegalArgumentException(
					"Expense amount should be more than zero");

		if (StringUtils.isBlank(expenseName))
			throw new IllegalArgumentException(
					"Expense name not may be empty string");

		BigDecimal amountByExpenseName = expenses.get(expenseName);
		if (amountByExpenseName == null)
			expenses.put(expenseName, amount);
		else
			expenses.put(expenseName, amountByExpenseName.add(amount));
	}

	@Override
	public String getReport() throws IllegalStateException {

		if (expenses.size() == 0)
			throw new IllegalStateException("Expenses list not may be empty");

		ColumnWidthInfo columnsWidth = calculateReportColumnsWidth();

		int nameColumnWidth = columnsWidth.getNameColumnWidth() + 2;
		int amountColumnWidth = columnsWidth.getAmountColumnWidth() + 2;

		int reportWidth = nameColumnWidth + amountColumnWidth + 1;
		String report = StringUtils.repeat("_", reportWidth) + '\n';

		report += getReportRow(EXPENSE_NAME_COLUMN_HEADER,
				EXPENSE_AMOUNT_COLUMN_HEADER, nameColumnWidth,
				amountColumnWidth);

		for (Entry<String, BigDecimal> expenseInfo : expenses.entrySet()) {
			report += getReportRow(expenseInfo.getKey(), expenseInfo.getValue()
					.setScale(2, BigDecimal.ROUND_HALF_UP).toString(),
					nameColumnWidth, amountColumnWidth);
		}

		return report;
	}

	private String getReportRow(String strNameColumn, String strAmountColumn,
			int nameColumnWidth, int amountColumnWidth) {
		String reportRow;
		reportRow = "|"
				+ strNameColumn
				+ StringUtils.repeat(" ",
						nameColumnWidth - strNameColumn.length() - 1);
		reportRow += "|";
		reportRow += StringUtils.repeat(" ", amountColumnWidth
				- strAmountColumn.length() - 1)
				+ strAmountColumn + "|" + '\n';

		return reportRow;

	}

	private ColumnWidthInfo calculateReportColumnsWidth() {

		short expenseNameColumnWidth = (short) EXPENSE_NAME_COLUMN_HEADER
				.length();
		short expenseAmountColumnWidth = (short) EXPENSE_AMOUNT_COLUMN_HEADER
				.length();

		for (Entry<String, BigDecimal> entry : expenses.entrySet()) {
			expenseNameColumnWidth = (short) Math.max(entry.getKey().length(),
					expenseNameColumnWidth);

			expenseAmountColumnWidth = (short) Math.max(entry.getValue()
					.setScale(2, BigDecimal.ROUND_HALF_UP).toString().length(),
					expenseAmountColumnWidth);

		}

		return new ColumnWidthInfo(expenseNameColumnWidth,
				expenseAmountColumnWidth);

	}

	@Override
	public void printReport() {

		if (printer == null)
			throw new IllegalStateException("Printer unavailable");

		printer.print(getReport());
	}

	@Override
	public void reset() {
		expenses.clear();
	}

	private static class ColumnWidthInfo {

		private final short nameColumnWidth;
		private final short amountColumnWidth;

		public ColumnWidthInfo(short nameColumnWidth, short amountColumnWidth) {
			super();
			this.nameColumnWidth = nameColumnWidth;
			this.amountColumnWidth = amountColumnWidth;
		}

		public short getNameColumnWidth() {
			return nameColumnWidth;
		}

		public short getAmountColumnWidth() {
			return amountColumnWidth;
		}
	}
}
